package com.quanyue.music.user.service.impl;

import com.quanyue.auto.cache.CacheService;
import com.quanyue.auto.util.AssertUtils;
import com.quanyue.auto.util.Md5Encoder;
import com.quanyue.auto.util.StringUtils;
import com.quanyue.music.domain.constant.CacheConstants;
import com.quanyue.music.domain.entity.Manager;
import com.quanyue.music.domain.entity.ManagerLoginError;
import com.quanyue.music.user.mapper.ManagerLoginErrorMapper;
import com.quanyue.music.user.mapper.ManagerMapper;
import com.quanyue.music.user.service.ManagerService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Service
public class ManagerServiceImpl implements ManagerService {

    @Autowired
    private ManagerMapper managerMapper;
    @Autowired
    private CacheService cacheService;
    @Autowired
    private ManagerLoginErrorMapper loginErrorMapper;

    @Override
    public Manager findById(String id) {
        return managerMap().get(id);
    }

    @Override
    public Manager sessionManager(String id) {
        Manager manager = managerMap().get(id);
        Manager sessionManager = new Manager();
        BeanUtils.copyProperties(manager, sessionManager);
        return sessionManager;
    }

    @Override
    public List<Manager> findAll(String id) {
        List<Manager> managerList = new ArrayList<>();
        Manager manager = managerMapper.findOne(id);
        if (!manager.getRole().equals("A")) {
            managerList.add(manager);
            return managerList;
        }
        managerList = managerMapper.findAllValid();
        if (managerList.isEmpty()) return Collections.EMPTY_LIST;
        return managerList;
    }

    @Override
    public Manager findByPassword(String username, String password) {
        // 判断是否超出登录次数限制
        Map<String, Integer> map = cacheService.get(CacheConstants.MANAGER_LOGIN_ERROR.name());
        ManagerLoginError record = loginErrorMapper.findByNameValid(username);
        if (record != null)
            throw new AssertUtils.ValidateException("纽约时间：" + record.getEndTime().plusMinutes(1) + "之后来登录 ^_^");
        // 查询是否是A类管理员
        Manager check = managerMapper.findByName(username, "A");
        // 验证用户名密码
        Manager manager = managerNameMap().get(username);
        if (manager != null && manager.getPassword().equals(password)) {
            map.put(username, 0);
            return manager;
        }
        // B和C类管理员记录错误登录次数
        if (check == null) {
            if (manager != null) {
                StringBuffer exist = new StringBuffer();
                map.forEach((k, v) -> {
                    if (username.equals(k)) {
                        map.put(k, v + 1);
                        // 记录key值是否已存在
                        exist.append("true");
                    }
                });
                if (!exist.toString().equals("true")) {
                    map.put(username, 1);
                }
                this.updateLoginError(username, map);
                throw new AssertUtils.ValidateException("(密码错误五次将被限制登录24个小时!)已错误：" + map.get(username) + "次");
            }
        }
        return null;
    }

    @Override
    public Manager add(Manager manager) {
        manager.setCreateTime(LocalDateTime.now());
        manager.setModifyTime(manager.getCreateTime());
        // just temporary
        manager.setPassword(Md5Encoder.md5(manager.getPassword()));
        manager.setDelFlag(false);
        manager.setStatus(true);
        managerMapper.insert(manager);
        this.updateCache(manager);
        return manager;
    }

    @Override
    public void update(Manager manager) {
        Manager checkManager = managerMapper.findOne(manager.getId());
        AssertUtils.notNull(checkManager, "没有该管理员");
        manager.setCreateTime(checkManager.getCreateTime());
        manager.setDelFlag(false);
        manager.setModifyTime(LocalDateTime.now());
        if (StringUtils.isNotBlank(manager.getPassword()))
            manager.setPassword(Md5Encoder.md5(manager.getPassword()));
        managerMapper.updateSelective(manager);
        this.updateCache(manager);
    }

    @Override
    public void updatePassword(String id, String current, String newPassword) {
        Manager manager = managerMap().get(id);
        AssertUtils.isTrue(manager.getPassword().equals(Md5Encoder.md5(current)), "旧密码不正确");
        Manager update = new Manager();
        update.setPassword(Md5Encoder.md5(newPassword));
        update.setId(manager.getId());
        managerMapper.updateSelective(update);
        manager.setPassword(newPassword);
    }

    @Override
    public void remove(String id) {
        Manager manager = new Manager();
        manager.setDelFlag(true);
        manager.setId(id);
        managerMapper.updateSelective(manager);
        manager = managerMap().get(id);
        managerNameMap().remove(manager.getUsername());
        managerMap().remove(id);
    }

    private void updateLoginError(String managerName, Map<String, Integer> map) {
        if (map.get(managerName) == 5) {
            ManagerLoginError loginError = new ManagerLoginError();
            loginError.setManagerName(managerName);
            loginError.setCreateTime(LocalDateTime.now());
            loginError.setEndTime(LocalDateTime.now().plusHours(24));
            loginError.setDelFlag(false);
            loginErrorMapper.insert(loginError);
            map.put(managerName, 0);
        }
    }

    private void updateCache(Manager manager) {
        managerMap().put(manager.getId(), manager);
        managerNameMap().put(manager.getUsername(), manager);
    }

    private Map<String, Manager> managerMap() {
        return cacheService.get(CacheConstants.MANAGER_ID.name());
    }

    private Map<String, Manager> managerNameMap() {
        return cacheService.get(CacheConstants.MANAGER_NAME.name());
    }

    @PostConstruct
    private void init() {
        Map<String, Manager> managers = managerMapper.findAllValid()
                .stream()
                .collect(Collectors.toMap(Manager::getId, m -> m));
        cacheService.put(CacheConstants.MANAGER_ID.name(), managers);
        cacheService.put(CacheConstants.MANAGER_NAME.name(), managerMapper.findAllValid()
                .stream()
                .collect(Collectors.toMap(Manager::getUsername, m -> m)));

        // 管理员登录错误次数记录
        Map<String, Integer> errorCount = new ConcurrentHashMap<>();
        cacheService.put(CacheConstants.MANAGER_LOGIN_ERROR.name(), errorCount);
    }
}
